#pragma once

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#define SHM_BLOCK_COUNT_MAX    (16)
#define SHM_MAGIC_NUM          (0x95279527)
#define SHM_BLOCK_SIZE_INVALID (0)

/* aligning multiples of 8 */
#define SHM_SIZE(size) (((size) + 7) / 8 * 8)

typedef struct _SHM_BLOCK_INFO
{
    unsigned int block_id; // enum SHM_BLOCK_ID
    unsigned int block_size;
    unsigned int align_block_size;
} SHM_BLOCK_INFO;

typedef enum _SHM_BLOCK_ID
{
    SHM_BLOCK_ID_TEST = 0,
} SHM_BLOCK_ID;

SHM_BLOCK_INFO *get_shm_blocks(void);
unsigned int get_shm_blocks_count(void);

unsigned int get_shm_align_header_size(void);
unsigned int get_shm_real_block_size(unsigned int block_id);
unsigned int get_shm_align_block_size(unsigned int block_id);
unsigned int get_shm_align_blocks_size(void);
unsigned int get_shm_align_total_size(void);

/**
 * @brief shm init(manager)
 * @Note please ensure that the shm_init_once() is called once.
 * @Note please ensure shm_blocks and shm_blocks_count is global variables.
 * @return int : 0 is success , other failed
 */
/*
// example
static SHM_BLOCK_INFO s_shm_blocks[] = {
   {SHM_BLOCK_ID_TEST, sizeof(SHM_BLOCK_TEST), SHM_SIZE(sizeof(SHM_BLOCK_TEST))},
};
static unsigned int s_shm_blocks_count = sizeof(s_shm_blocks) / sizeof(s_shm_blocks[0]);
shm_init_once(s_shm_blocks, s_shm_blocks_count);
*/
int shm_init_once(SHM_BLOCK_INFO *shm_blocks, unsigned int shm_blocks_count);

/**
 * @brief shm init(other)
 * @return int : 0 is success , other failed
 */
int shm_init(void);

/**
 * @brief lock one block of shm.
 * @param[in] block_id : the block id of shm
 * @return int : 0 is success , other failed
 */
int shm_lock(unsigned int block_id);

/**
 * @brief unlock one block of shm.
 * @param[in] block_id : the block id of shm
 * @return int : 0 is success , other failed
 */
int shm_unlock(unsigned int block_id);

/**
 * @brief get addr of one block data.
 * You should call shm_lock() before call shm_get_block_addr(), and call shm_unlock() after.
 * @param[in] block_id : the block id of shm
 * @return void * : if fail, return NULL; if success, return the addr of the block data.
 */
void *shm_get_block_addr(unsigned int block_id);

/**
 * @brief copy one block data from shm to usr buffer.
 * @param[in] block_id : the block id of shm
 * @param[in] data_len : length of data_buf
 * @param[out] data_buf : usr buffer
 * @return int : 0 is success , other failed
 */
int shm_get_block_data(unsigned int block_id, unsigned int data_len, void *data_buf);

/**
 * @brief copy one block data from usr buffer to shm.
 * @param[in] block_id : the block id of shm
 * @param[in] data_len : length of data_buf
 * @param[out] data_buf : usr buffer
 * @return int : 0 is success , other failed
 */
int shm_set_block_data(unsigned int block_id, unsigned int data_len, void *data_buf);

#ifdef __cplusplus
}
#endif
